<!--
 * @Author: your name
 * @Date: 2022-03-05 01:00:55
 * @LastEditTime: 2022-04-10 09:59:46
 * @LastEditors: Please set LastEditors
 * @Description: 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 * @FilePath: \blog_web\src\components\upload\upload.vue
-->
<!--
  项目公共组件：文件上传组件
-->
<template>
  <el-upload
    action="#"
    :multiple="multiple"
    :list-type="listType"
    :auto-upload="autoUpload"
    :before-upload="beforeUpload"
    :accept="accept"
    :http-request="checkedFile"
    :show-file-list="showList"
    :before-remove="beforeRemove"
    :disabled="disabled"
    :on-change="onChange"
  >
    <!-- 如果当前组件属于图片上传模式，组件显示为上传图片的的显示样式 -->
    <template v-if="isImgLoadModel">
      <slot>
        <div class="upload-style">
          <i class="el-icon-plus"></i>
        </div>
      </slot>
    </template>

    <!-- 否则属于普通的文件上传模式，组件显示为文件上传的显示样式 -->
    <template v-else>
      <slot>
        <!-- 上传按钮 -->
        <div class="upload-btn-box" slot="default">
          <el-button size="small" icon="el-icon-plus">{{ btnText }}</el-button>
        </div>
        <!-- 上传提示文本 -->
        <div class="el-upload__tip" slot="tip">{{ uploadTip }}</div>
      </slot>
    </template>
  </el-upload>
</template>
<script>
import axios from 'axios'
import request from '@/request'
export default {
  props: {
    // 是否可以多选
    multiple: { type: Boolean, default: true },

    // 文件列表的类型
    listType: { type: String, default: 'text' },

    // 是否自动上传
    autoUpload: { type: Boolean, default: true },

    // 可以接受的文件类型,默认为所有类型
    accept: { type: String, default: '*' },

    // 后端给的上传的地址，默认珠江租赁项目的话目前该为：
    url: { type: String, default: 'http://106.52.230.201:8000/file/upload' },

    // 文件的最大大小
    maxSize: { type: Number, default: 100 * 1024 * 1024 },

    // 大于10MB这个大小的文件使用分块上传(后端可以支持断点续传) 10月暂时不考虑这个
    multiUploadSize: { type: Number, default: 10 * 1024 * 1024 },

    // 每块文件大小  10月暂时不考虑这个
    eachSize: { type: Number, default: 10 * 1024 * 1024 },

    // 用户传递的参数
    params: { type: Object, default: () => {} },

    // 是否展示文件列表  如果是文件的话需要设置为true
    showList: { type: Boolean, default: false },

    // 是否禁用
    disabled: { type: Boolean, default: false },

    // 上传按钮的按钮文本
    btnText: {
      type: String,
      default: '上传材料'
    },

    // 上传提示文本
    uploadTip: {
      type: String,
      default: '只支持上传jpg/png格式'
    },

    /**
     * 图片上传模式
     * @author 阮中楠
     * @description
     * 是否是图片上传模式？默认为否。
     * 设计理由：文件上传组件在不同的情况下，应该显示显示不同的样式。具体使用方式见文档。
     */
    isImgLoadModel: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      // maxSize: 100 * 1024 * 1024, // 上传最大文件限制(100MB)
      // multiUploadSize: 10 * 1024 * 1024, // 大于10MB这个大小的文件使用分块上传(后端可以支持断点续传)
      // eachSize: 10 * 1024 * 1024, // 每块文件大小
      requestCancelQueue: [], // 请求方法队列（调用取消上传
      delFileIds: []
    }
  },
  methods: {
    // 覆盖默认的上传行为，可以自定义上传的实现
    async checkedFile(options) {
      console.log('执行了checkedFile')
      const {
        maxSize,
        multiUploadSize,
        accept,
        getSize,
        splitUpload,
        singleUpload
      } = this
      const { file, onProgress, onSuccess, onError } = options
      console.log('options', options, accept, this)
      // 检查文件是否符合要求
      if (accept != '*') {
        const fileSuffix = file.name.substring(file.name.lastIndexOf('.') + 1)
        let str = accept.replace(/\./g, '').split(',')
        console.log('可以接受的值为:', str)
        if (str.indexOf(fileSuffix) === -1) {
          this.$message.error('上传的文件不符合要求')
          return false
        }
      }
      // 检查文件的大小
      if (file.size > maxSize) {
        return this.$message({
          message: `您选择的文件大于${getSize(maxSize)}`,
          type: 'error'
        })
      }
      // 根据文件的大小确定上传的方式
      const uploadFunc =
        file.size > multiUploadSize ? splitUpload : singleUpload
      try {
        await uploadFunc(file, onProgress)
        this.$message({
          message: '上传成功',
          type: 'success'
        })
        onSuccess((e) => {
          // 成功回调
          this.$emit('success', {})
          console.log(e, 'success upload')
        })
      } catch (e) {
        console.error(e)
        this.$message({
          message: e.message,
          type: 'error'
        })
        onError()
      }
      const prom = new Promise((resolve, reject) => {})
      prom.abort = () => {}
      return prom
    },
    // 格式化文件大小显示文字
    getSize(size) {
      return size > 1024
        ? size / 1024 > 1024
          ? size / (1024 * 1024) > 1024
            ? (size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
            : (size / (1024 * 1024)).toFixed(2) + 'MB'
          : (size / 1024).toFixed(2) + 'KB'
        : size.toFixed(2) + 'B'
    },
    // 单文件直接上传
    singleUpload(file, onProgress) {
      // { file, uid: file.uid, fileName: file.fileName },
      return this.postFile(
        {
          file,
          isSlice: 0,
          fileId: file.uid,
          fileName: file.name,
          fileType: file.type,
          storageType: 'local',
          ...this.params
        },
        onProgress
      )
    },
    // 大文件分块上传
    splitUpload(file, onProgress) {
      // return console.log(file,"file")
      return new Promise(async (resolve, reject) => {
        try {
          const { eachSize } = this
          const chunks = Math.ceil(file.size / eachSize)
          const fileChunks = await this.splitFile(file, eachSize, chunks)
          let currentChunk = 0
          for (let i = 0; i < fileChunks.length; i++) {
            // 服务端检测已经上传到第currentChunk块了，那就直接跳到第currentChunk块，实现断点续传
            console.log(currentChunk, i)
            if (Number(currentChunk) === i) {
              // 每块上传完后则返回需要提交的下一块的index
              currentChunk = await this.postFile(
                {
                  fileSize: file.size,
                  // chunked: true,
                  // chunk: i,
                  // chunks,
                  // eachSize,
                  fileName: file.name,
                  fullSize: file.size,
                  uid: file.uid,
                  fileType: file.type,
                  file: fileChunks[i],
                  storageType: 'local',
                  isSlice: 1,
                  sliceSeq: i,
                  ...this.params
                },
                onProgress
              )
            }
          }
          resolve()
        } catch (e) {
          reject(e)
        }
      })
    },
    // 文件分块,利用Array.prototype.slice方法
    splitFile(file, eachSize, chunks) {
      return new Promise((resolve, reject) => {
        try {
          setTimeout(() => {
            const fileChunk = []
            for (let chunk = 0; chunks > 0; chunks--) {
              fileChunk.push(file.slice(chunk, chunk + eachSize))
              chunk += eachSize
            }
            resolve(fileChunk)
          }, 0)
        } catch (e) {
          console.error(e)
          reject(new Error('文件切块发生错误'))
        }
      })
    },
    // 提交文件方法,将参数转换为FormData, 然后通过axios发起请求
    postFile(param, onProgress) {
      console.log('提交文件方法====', param)
      const formData = new FormData()
      for (let p in param) {
        formData.append(p, param[p])
      }
      const { requestCancelQueue } = this
      const config = {
        cancelToken: new axios.CancelToken(function executor(cancel) {
          if (requestCancelQueue[param.uid]) {
            requestCancelQueue[param.uid]()
            delete requestCancelQueue[param.uid]
          }
          requestCancelQueue[param.uid] = cancel
        }),
        onUploadProgress: (e) => {
          if (param.chunked) {
            e.percent = Number(
              (
                ((param.chunk * (param.eachSize - 1) + e.loaded) /
                  param.fullSize) *
                100
              ).toFixed(2)
            )
          } else {
            e.percent = Number(((e.loaded / e.total) * 100).toFixed(2))
          }
          onProgress(e)
        }
      }
      return request.upload(this.url, formData, config).then((rs) => {
        // rs.data
        this.$emit('success', rs, param.fileName)
      })
    },
    onChange(file, fileList) {
      console.log('子组件', file, fileList)
      this.$emit('onChange', file.raw)
    },
    //移除文件的
    beforeRemove(file, fileList) {
      // console.log(file, fileList);
      // return this.$confirm(`将删除${file.name}, 是否继续?`, "删除").then(() => {
      //   console.log("用户点击了确定", fileList.findIndex(item => item.uid === file.uid));
      //   const index = fileList.findIndex(item => item.uid === file.uid)
      //   this.$emit("removeFile", index);
      // });
    },
    // 文件上传前的钩子函数
    beforeUpload(file) {
      console.log('文件上传前的钩子函数', file)
      // const fileSuffix = file.name.substring(file.name.lastIndexOf("."));
      // console.log('文件上传',fileSuffix);
      // 检查文件是否符合要求
      // if (this.accept != "*") {
      //   const fileSuffix = file.name.substring(file.name.lastIndexOf(".") + 1);
      //   let str = accept.replace(/\./g, "").split(",");
      //   console.log('可以接受的值为:',str);
      //   if (str.indexOf(fileSuffix) === -1) {
      //     this.$message.error("上传的文件不符合要求");
      //     return false;
      //   }
      // }
    }
  }
}
</script>
<style lang="scss" scoped>
/deep/ .el-upload__tip {
  margin-top: 0;
}
.upload-btn-box {
  text-align: left;
}
.upload-style {
  width: 100px;
  height: 100px;
  border: 1px dashed #ccc;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>
